#include "Winmain.h"
#include "RPCCode.h"
#define _WIN32_DCOM
using namespace std;
#include <math.h>
#include <comdef.h>
#include <Wbemidl.h>

# pragma comment(lib, "wbemuuid.lib")



HDC					hDC=NULL;		//Device Context
HGLRC				hRC=NULL;		//Rendering Context
HWND				hWnd=NULL;
HINSTANCE			hInstance;
RECT				rect;
HRESULT				hres;
static char			title[20];
bool				keys[256];						// Array Used For The Keyboard Routine

HANDLE				hPhysManThread;
DWORD				dwPhysManThreadId;

HANDLE				hRPCThread;
DWORD				dwRPCThread;

HANDLE				hPerfMonThread;
DWORD				dwPerfMonThread;

HANDLE				hPhysManSignalUp;
HANDLE				hPhysManSignalDown;
HANDLE				hDrawOn;
HANDLE				hDrawOff;



CRITICAL_SECTION	CriticalSection;
CRITICAL_SECTION	CriticalSectionPerfData;
//More globals. Specific to Collisions/GL
CFPSDetector FPSMan;
static float frametime(0);

int GetValues(IWbemServices *pSvc,ULONG *ulVal ,int n)
{
	int nError = 0;
	 // Step 6: --------------------------------------------------
    // Use the IWbemServices pointer to make requests of WMI ----

    // For example, get the name of the operating system
    IEnumWbemClassObject* pEnumerator = NULL;
    HRESULT hres = pSvc->ExecQuery(
        bstr_t("WQL"), 
        bstr_t("SELECT * FROM Win32_PerfRawData_PerfOS_Processor"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
        NULL,
        &pEnumerator);
    
    if (FAILED(hres))
    {
        cout << "Query for operating system name failed."
            << " Error code = 0x" 
            << hex << hres << endl;

        return 1;               // Fxn has failed.
    }

    // Step 7: -------------------------------------------------
    // Get the data from the query in step 6 -------------------
 
    IWbemClassObject *pclsObj;
    ULONG uReturn = 0;
	int nCtr = 0;
  
    while (nCtr<n)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, 
            &pclsObj, &uReturn);

        if(0 == uReturn)
        {
            break;
        }

        VARIANT vtProp;
        VariantInit(&vtProp);

		hr = pclsObj->Get(L"PercentProcessorTime", 0, &vtProp, 0, 0);
		ulVal[nCtr] = _wtof(vtProp.bstrVal);
        VariantClear(&vtProp);
		hr = pclsObj->Get(L"TimeStamp_Sys100NS", 0, &vtProp, 0, 0);
		ulVal[nCtr+1] = _wtof(vtProp.bstrVal);
		VariantClear(&vtProp);
		nCtr+=2;
    }
	pclsObj->Release();
	pEnumerator->Release();
	return nError;
}


int WINAPI WinMain(	HINSTANCE	hInstance,
					HINSTANCE	hPrevInstance,
					LPSTR		lpCmdLine,
					int			nCmdShow)			// Window Show State
{
	MSG		msg;									// Windows Message Structure
	BOOL	done=FALSE;								// Bool Variable To Exit Loop
	//hInsta = hInstance;
	//float cumulativefps(0);
	//int framecount(0);
	//float fps(0);
	
	//AttachToWMI();
	// Create Our OpenGL Window

	//PhysMan = new PhysicsManager(12.0f, 8.0f, NUMCPU);
	//FPSMan = new CFPSDetector();

	InitializeCriticalSection(&CriticalSection);
	InitializeCriticalSection(&CriticalSectionPerfData);

	CreateGLWindow("BallTest",1024,768,32); //Default settings. 1024x768 at 32 bit color
	//PhysMan.increaseBalls();

	hDrawOn = CreateEvent(NULL, TRUE, FALSE, NULL);
	hDrawOff = CreateEvent(NULL, TRUE, FALSE, NULL);

	hPerfMonThread	= CreateThread(NULL, NULL, &ThreadPerfMon, NULL, NULL, &dwPerfMonThread);
	hPhysManThread	= CreateThread(NULL, NULL, &ThreadPhysMan, NULL, NULL, &dwPhysManThreadId);
	hRPCThread		= CreateThread(NULL, NULL, &RPCThread, NULL, NULL, &dwRPCThread);

	while(!done)									// Loop That Runs While done=FALSE
	{
		//Sleep(10);
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	// Is There A Message Waiting?
		{
			
			if (msg.message==WM_QUIT)				// Have We Received A Quit Message?
			{
				done=TRUE;							// If So done=TRUE
			}
			else									// If Not, Deal With Window Messages
			{
				TranslateMessage(&msg);				// Translate The Message
				DispatchMessage(&msg);				// Dispatch The Message
			}
		}
		else										// If There Are No Messages
		{
			ResetEvent(hDrawOn);
			try{
				DrawScene();
			}
			catch(...){

			}
			SetEvent(hDrawOff);

		}
	}

	
	// Shutdown
	KillGLWindow();									// Kill The Window
	

	TerminateThread(hPhysManThread, NULL);
	DeleteCriticalSection(&CriticalSection);
	DeleteCriticalSection(&CriticalSectionPerfData);
	return (msg.wParam);							// Exit The Program
}

LRESULT CALLBACK WndProc(	HWND	hWnd,
							UINT	uMsg,
							WPARAM	wParam,
							LPARAM	lParam)			// Additional Message Information
{
	
	static LPPOINT pt;

	switch (uMsg)									// Check For Windows Messages
	{
		/*
		case WM_ACTIVATE:							// Watch For Window Activate Message
		{
			if (HIWORD(wParam) > 0)					// Check Minimization State
			{
				active=TRUE;
				ShowCursor(false);// Program Is Active

			}
			else
			{
				active=FALSE;						// Program Is No Longer Active
				ShowCursor(true);
			}

			return 0;								// Return To The Message Loop
		}
		*/

		case WM_CREATE:
		{
			//test = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, NewGameMenuProc, (LPARAM)&MazeSettings);
			return 0;
		}

		case WM_SYSCOMMAND:							// Intercept System Commands
		{
			switch (wParam)							// Check System Calls
			{
				case SC_SCREENSAVE:					// Screensaver Trying To Start?
				case SC_MONITORPOWER:				// Monitor Trying To Enter Powersave?
				return 0;							// Prevent From Happening
			}
			break;									// Exit
		}
		
		case WM_COMMAND:
			{
				//switch(wParam)
				//{
				//case ID_FILE_EXIT:
				//	{
				//		PostQuitMessage(0); 
				//		return 0;
				//	}
				//case ID_FILE_NEWGAME:
				//	{
				//		test = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, NewGameMenuProc, (LPARAM)&MazeSettings);
				//								
				//		active = true;
				//		ShowCursor(FALSE);
				//		SetupMazeIDs();
				//		Camera.InitialPosition(EntranceX*CUBESIZE, QUARTERCUBE, -200.0f, 180.0f);

				//		return 0;
				//	}
				//case ID_GAME_SETTINGS:
				//	{
				//		test = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG2), hWnd, SettingsMenuProc, (LPARAM)&MazeSettings);
				//								
				//		active = true;
				//		ShowCursor(FALSE);
				//		SetupMazeIDs();
				//		return 0;
				//	}
				//case ID_HELP_ABOUT:
				//	{
				//		test = DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG4), hWnd, AboutProc);
				//		active = true;
				//		ShowCursor(FALSE);
				//		return 0;
				//	}
				//case ID_HELP_HTMLHELP:
				//	{
				//		HtmlHelp(hWnd, "ArtsMaze3DHelp.chm", HH_DISPLAY_TOPIC, 0);

				//		return 0;
				//	}
				//}
				return 0;
			}

		case WM_CLOSE:								// Did We Receive A Close Message?
		{
			PostQuitMessage(0);						// Send A Quit Message
			return 0;								// Jump Back
		}

		case WM_KEYDOWN:							// Is A Key Being Held Down?
		{
			keys[wParam] = TRUE;					// If So, Mark It As TRUE
			return 0;								// Jump Back
		}

		case WM_KEYUP:								// Has A Key Been Released?
		{
			keys[wParam] = FALSE;					// If So, Mark It As FALSE
			return 0;								// Jump Back
		}
		/*
		case WM_RBUTTONDOWN:
		{
			
			rmousedown = true;
			break;
		}

		case WM_RBUTTONUP:
		{
			//static bool lmousedown;
			rmousedown = false;
			break;
		}
		*/
		/*
		case WM_MOUSEMOVE:
			{
				//CheckMouse();
				//return 0;
				break;
			}
			*/
		//case WM_SYSCHAR:
		//{
		//	switch(wParam)
		//	{
		//	case VK_F1:
		//		{


		//		}

		//	}
		//	return 0;
		//}
		case WM_CHAR:
		{
			switch(wParam)
			{
			case VK_ESCAPE:
				{

					return 0;
				}
			case 'A':
			case 'a':
				{
					SetEvent(hPhysManSignalUp);
					//PhysMan.increaseBalls();
					return 0;
				}

			case 'S':
			case 's':
				{
					SetEvent(hPhysManSignalDown);
					return 0;
				}

			case 'F':
			case 'f':
				{

					return 0;
				}
			case 'R':
			case 'r':
				{

					return 0;
				}
			}
			return 0;
		}
		case WM_SIZE:								// Resize The OpenGL Window
		{
			UINT newx = LOWORD(lParam);
			UINT newy = HIWORD(lParam);

			ChangeSize(newx,newy);  // LoWord=Width, HiWord=Height

			SwapBuffers(hDC);
			return 0;								// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

bool CreateGLWindow(char* title, int width, int height, int bits)
{
	unsigned int	PixelFormat;			// Holds The Results After Searching For A Match
	WNDCLASSEX		wc;						// Windows Class Structure
	DWORD			dwExStyle;				// Window Extended Style
	DWORD			dwStyle;				// Window Style
	RECT			WindowRect;				// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left=(long)0;				// Set Left Value To 0
	WindowRect.right=(long)width;			// Set Right Value To Requested Width
	WindowRect.top=(long)0;					// Set Top Value To 0
	WindowRect.bottom=(long)height;			// Set Bottom Value To Requested Height


	hInstance			= GetModuleHandle(NULL);				// Grab An Instance For Our Window
	wc.cbSize			= sizeof(WNDCLASSEX);
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
	wc.lpfnWndProc		= WndProc;								// WndProc Handles Messages
	wc.cbClsExtra		= 0;									// No Extra Window Data
	wc.cbWndExtra		= 0;									// No Extra Window Data
	wc.hInstance		= hInstance;							// Set The Instance
	wc.hIcon			= NULL;//LoadIcon(hInstance, (LPCSTR)IDI_ICON1);			// Load The Default Icon
	wc.hIconSm			= NULL;//LoadIcon(hInstance, (LPCSTR)IDI_ICON1);		  // windows logo small icon
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
	wc.hbrBackground	= NULL;									// No Background Required For GL
	wc.lpszMenuName		= NULL;//(LPCSTR)IDR_MENU1;		// We Don't Want A Menu
	wc.lpszClassName	= "Balls";								// Set The Class Name

	if (!RegisterClassEx(&wc))									// Attempt To Register The Window Class
	{
		MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;			// Window Extended Style
		dwStyle=WS_OVERLAPPEDWINDOW;							// Windows Style
	//}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To True Requested Size

	// Create The Window
	hWnd=CreateWindowEx(	dwExStyle,							// Extended Style For The Window
							"Balls",							// Class Name
							title,								// Window Title
							dwStyle |							// Defined Window Style
							WS_CLIPSIBLINGS |					// Required Window Style
							WS_CLIPCHILDREN,					// Required Window Style
							0, 0,								// Window Position
							WindowRect.right-WindowRect.left,	// Calculate Window Width
							WindowRect.bottom-WindowRect.top,	// Calculate Window Height
							NULL,								// No Parent Window
							NULL,								// No Menu
							hInstance,							// Instance
							NULL);								// Dont Pass Anything To WM_CREATE

	static	PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		bits,										// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		16,											// 16Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};
	
	hDC=GetDC(hWnd);							// Get A Device Context?
	
	PixelFormat=ChoosePixelFormat(hDC,&pfd);	// Find A Matching Pixel Format

	SetPixelFormat(hDC,PixelFormat,&pfd);		// Set The Pixel Format


	hRC=wglCreateContext(hDC);				// Get A Rendering Context?

	wglMakeCurrent(hDC,hRC);				// Activate The Rendering Context


	ShowWindow(hWnd,SW_SHOW);						// Show The Window
	SetForegroundWindow(hWnd);						// Slightly Higher Priority
	SetFocus(hWnd);									// Sets Keyboard Focus To The Window
	ChangeSize(width, height);					// Set Up Our Perspective GL Screen
	//SetCursorPos(CenterX, CenterY);
	SetupRC();								// Initialize Our Newly Created GL Window
	return TRUE;									// Success
}

void KillGLWindow(void)								// Properly Kill The Window
{

	ChangeDisplaySettings(NULL,0);					// If So Switch Back To The Desktop
	ShowCursor(TRUE);								// Show Mouse Pointer

	if (hRC)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,TEXT("Release Of DC And RC Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL,TEXT("Release Rendering Context Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;										// Set RC To NULL
	}

	if (hDC && !ReleaseDC(hWnd,hDC))					// Are We Able To Release The DC
	{
		MessageBox(NULL,TEXT("Release Device Context Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
		hDC=NULL;										// Set DC To NULL
	}

	if (hWnd && !DestroyWindow(hWnd))					// Are We Able To Destroy The Window?
	{
		MessageBox(NULL,TEXT("Could Not Release hWnd."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;										// Set hWnd To NULL
	}

	if (!UnregisterClass(TEXT("Balls"),hInstance))			// Are We Able To Unregister Class
	{
		MessageBox(NULL,TEXT("Could Not Unregister Class."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;									// Set hInstance To NULL
	}
}

void CheckKeys(void)
{
	if(keys[VK_UP])
	{
		//Cam.ChangePitch(5.0f);
		
	}
	
	if(keys[VK_DOWN])
	{
		//Cam.ChangePitch(-5.0f);
	}

	if(keys[VK_LEFT])
	{
		//Cam.ChangeHeading(-5.0f);
	}
	
	if(keys[VK_RIGHT])
	{
		//Cam.ChangeHeading(5.0f);
	}
	
	if(keys['W'] == TRUE)
	{

	}

	if(keys['S'] == TRUE)
	{

	}
	/*
	if(keys[VK_F9] == TRUE)
	{

		Camera.ToggleLockCameraInMaze();
	}

	if(keys[VK_F1] == true)
	{

	}
	*/

}

void ChangeSize(int width, int height)		// Resize And Initialize The GL Window
{
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}

	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(float)width/(float)height,1.0f,200.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix

	//GetClientRect(hWnd, &rect);
	
}

void SetupRC(void)										// All Setup For OpenGL Goes Here
{
	glEnable(GL_DEPTH_TEST);
	//glFrontFace(GL_CCW);
	//glClearDepth(1.0f);									// Depth Buffer Setup
	//glClearStencil(0);									// Clear The Stencil Buffer To 0
	//glDepthFunc(GL_LEQUAL);
	glShadeModel(GL_SMOOTH);
	//glShadeModel(GL_PHONG_WIN);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_NORMALIZE);
	//glEnable(GL_HINT);
	//glEnable(GL_POLYGON_SMOOTH);
	//glEnable(GL_BLEND);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glClearColor(0.1f, 0.1f, 0.1f, 1.0f );

	glDepthFunc(GL_LEQUAL);
	
	glPointSize(8.0f);
	glEnable(GL_POINT_SMOOTH);
}

void DrawScene(){
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, -30.0f);
	GLfloat ambientColor[] = {0.5f, 0.5f, 0.5f, 1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
	GLfloat lightColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat lightPos[] = {1.0f, 0.2f, 0.0f, 0.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

	//Draw the balls as 10 pixel points
	glPushMatrix();
	glBegin(GL_POINTS);
	
	for(unsigned int i = 0; i < PhysMan._balls.size(); i++) {
		Ball* ball = PhysMan._balls[i];
		glColor3f(ball->color[0], ball->color[1], ball->color[2]);
		glVertex3f(ball->pos[0], ball->pos[1], ball->pos[2]);		
	}
	
	glEnd();
	glPopMatrix();

	SwapBuffers(hDC);
}



//Threads. Needed to be global

DWORD WINAPI ThreadBallBallWork( LPVOID lpParam ){

	int * WorkerId = (int*)lpParam;
	vector<BallPair> * pLocalJobs = &(PhysMan).BallBallWorkArray[*WorkerId];//(vector<BallPair>*)lpParam;

	DWORD dwWaitResult;
	int numPairs(0);
	extern PhysicsManager PhysMan;
	bool error(0);
	//HANDLE RealHandle;

	//DuplicateHandle(GetCurrentProcess(),
	//			GetCurrentThread(),
	//			GetCurrentProcess(),
	//			&RealHandle,
	//			0,
	//			FALSE,
	//			DUPLICATE_SAME_ACCESS);
	while(1){

		dwWaitResult = WaitForSingleObject(
			PhysMan.hStartEvents[*WorkerId],
			INFINITE);
		ResetEvent(PhysMan.hStartEvents[*WorkerId]);

		int num_collide = pLocalJobs->size();
		if(num_collide > 0){
			for(unsigned int i = 0; i < num_collide; i++) {
				BallPair bp = (*pLocalJobs)[i];
		
				Ball* b1 = bp.ball1;
				Ball* b2 = bp.ball2;
				if (PhysMan.testBallBallCollision(b1, b2)) {
					//Make the balls reflect off of each other
					Vec3f displacement = (b1->pos - b2->pos).normalize();
					b1->v -= 2 * displacement * b1->v.dot(displacement);
					b2->v -= 2 * displacement * b2->v.dot(displacement);
				}
			}
			pLocalJobs->clear();
			
			//PhysMan.ThreadsDoneCount++;
		}
		SetEvent(PhysMan.hDoneEvents[*WorkerId]);
	}
	return 1;
}

DWORD WINAPI ThreadBallWallWork( LPVOID lpParam ){
	int * pWorkerId = (int*)lpParam;
	int WorkerId = *pWorkerId % PhysMan.NUM_JOBS;
	vector<BallWallPair> * pLocalJobs = &(PhysMan).BallWallWorkArray[WorkerId];//(vector<BallPair>*)lpParam;
	//int test = PhysMan.BallWallWorkArray[*WorkerId].capacity;

	DWORD dwWaitResult;
	int numPairs(0);
	extern PhysicsManager PhysMan;
	bool error(0);
	//HANDLE RealHandle;

	//DuplicateHandle(GetCurrentProcess(),
	//			GetCurrentThread(),
	//			GetCurrentProcess(),
	//			&RealHandle,
	//			0,
	//			FALSE,
	//			DUPLICATE_SAME_ACCESS);
	while(1){

		dwWaitResult = WaitForSingleObject(
			PhysMan.hStartEvents[*pWorkerId],
			INFINITE);
		ResetEvent(PhysMan.hStartEvents[*pWorkerId]);

		int num_collide = pLocalJobs->size();
		if(num_collide > 0){
			for(unsigned int i = 0; i < num_collide; i++) {
				BallWallPair bwp = (*pLocalJobs)[i];
		
				Ball* b = bwp.ball;
				Wall w = bwp.wall;
				if (PhysMan.testBallWallCollision(b, w)) {
					//Make the balls reflect off the wall
					Vec3f dir = (PhysMan.wallDirection(w)).normalize();
					b->v -= 2 * dir * b->v.dot(dir);
				}
			}
			pLocalJobs->clear();
			//PhysMan.ThreadsDoneCount++;
		}
		SetEvent(PhysMan.hDoneEvents[*pWorkerId]);
	}
	return 1;
}

DWORD WINAPI ThreadBallMove( LPVOID lpParam ){
	int * WorkerId = (int*)lpParam;
	DWORD dwWaitResult;

	while(1){
		dwWaitResult = WaitForSingleObject(
			PhysMan.hStartBallMove[*WorkerId],
			INFINITE);
		ResetEvent(PhysMan.hStartBallMove[*WorkerId]);

		unsigned int num_balls = PhysMan.BallsToMove[*WorkerId].size();
		if(num_balls > 0){
			for(unsigned int i(0); i<num_balls; i++){
				Ball * ball = PhysMan.BallsToMove[*WorkerId].at(i);
				Vec3f oldPos = ball->pos;
				ball->pos += ball->v * (1.0f/frametime); //dt = current frame rate.
				//EnterCriticalSection(&CriticalSection);
				PhysMan._octree->ballMoved(ball, oldPos);
				//LeaveCriticalSection(&CriticalSection);
			}
					//Ball* ball = balls[i];
			//Vec3f oldPos = ball->pos;
			//ball->pos += ball->v * (1.0f/dt);
			//octree->ballMoved(ball, oldPos);
			PhysMan.BallsToMove[*WorkerId].clear();
		}

		
		SetEvent(PhysMan.hStopBallMove[*WorkerId]);
	}

	return 1;
}

DWORD WINAPI ThreadPhysMan( LPVOID lpParam){
	float cumulativefps(0);
	int framecount(0);
	float fps(0);
	DWORD dwWaitResult;
	hPhysManSignalUp = CreateEvent(NULL, TRUE, FALSE, NULL);
	hPhysManSignalDown = CreateEvent(NULL, TRUE, FALSE, NULL);


	while(1){
		dwWaitResult = 0;
		dwWaitResult = WaitForSingleObject(
			hPhysManSignalUp,
			0);
		if(dwWaitResult == WAIT_OBJECT_0){
			EnterCriticalSection(&CriticalSection);
			PhysMan.increaseBalls();
			LeaveCriticalSection(&CriticalSection);
		}
		ResetEvent(hPhysManSignalUp);
		dwWaitResult = 0;
		dwWaitResult = WaitForSingleObject(
			hPhysManSignalDown,
			0);
		if(dwWaitResult == WAIT_OBJECT_0){
			EnterCriticalSection(&CriticalSection);
			PhysMan.decreaseBalls();
			LeaveCriticalSection(&CriticalSection);
		}
		ResetEvent(hPhysManSignalDown);
		
		framecount++;
		frametime = FPSMan.GetFPS();
		cumulativefps += frametime;
		FPSMan.End();
		FPSMan.Start();
		fps = cumulativefps/(float)framecount;
		if(framecount >= fps/4){
			
			sprintf(title, "FPS: %.2f, BALLS: %d", fps, PhysMan._balls.size());
			SetWindowText(hWnd, title);
			framecount = 0;
			cumulativefps = 0;
			SetEvent(hDrawOn);
			WaitForSingleObject(hDrawOff, INFINITE);
			ResetEvent(hDrawOff);
		}

		PhysMan.CalcFrame(frametime);
	}

	return 1;
}

DWORD WINAPI RPCThread( LPVOID lpParam){
	RPC_STATUS status;

	status = RpcServerUseProtseqEp(
		reinterpret_cast<unsigned char*>("ncacn_ip_tcp"), // Use TCP/IP protocol.
		RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // Backlog queue length for TCP/IP.
		reinterpret_cast<unsigned char*>("4747"), // TCP/IP port to use.
		NULL); // No security.

	if (status){
		cout << "Failure" << endl;
		exit(status);
	}
	status = RpcServerRegisterIf2(
		RemoteCall_v1_0_s_ifspec, // Interface to register.
		NULL, // Use the MIDL generated entry-point vector.
		NULL, // Use the MIDL generated entry-point vector.
		RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH, // Forces use of security callback.
		RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Use default number of concurrent calls.
		(unsigned)-1, // Infinite max size of incoming data blocks.
		SecurityCallback); // Naive security callback.

	if (status){
		cout << "Failure." << endl;
		exit(status);
	}

	status = RpcServerListen(
		1, // Recommended minimum number of threads.
		RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Recommended maximum number of threads.
		FALSE); // KEEP LISTENING. Client will stop us!


	return 0;
}

DWORD WINAPI ThreadPerfMon( LPVOID lpParam ){
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------
	int nproc=1;
	SYSTEM_INFO sysinfo; 
	int i(0);

    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x" 
            << hex << hres << endl;
        return 1;                  // Program has failed.
    }

	//cout << "Please enter the # of processors : ";

	//cin>>nproc;
	GetSystemInfo(&sysinfo);
	nproc = sysinfo.dwNumberOfProcessors; //Number of processors is important for physics engine


	ULONG *ulinitVal = new ULONG[(nproc+1)*2];//+1 for the total
	//int i=0;
	ULONG *ulVal = new ULONG[(nproc+1)*2];//+1 for the total
    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    // Note: If you are using Windows 2000, you need to specify -
    // the default authentication credentials for a user by using
    // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
    // parameter of CoInitializeSecurity ------------------------

    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );

                      
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x" 
            << hex << hres << endl;
        CoUninitialize();
        return 1;                    // Program has failed.
    }
    
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;

    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
 
    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object."
            << " Err code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;                 // Program has failed.
    }

    // Step 4: -----------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;
	
    // Connect to the root\cimv2 namespace with
    // the current user and obtain pointer pSvc
    // to make IWbemServices calls.
    hres = pLoc->ConnectServer(
         _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
         NULL,                    // User name. NULL = current user
         NULL,                    // User password. NULL = current
         0,                       // Locale. NULL indicates current
         NULL,                    // Security flags.
         0,                       // Authority (e.g. Kerberos)
         0,                       // Context object 
         &pSvc                    // pointer to IWbemServices proxy
         );
    
    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
             << hex << hres << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;                // Program has failed.
    }

    //cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


    // Step 5: --------------------------------------------------
    // Set security levels on the proxy -------------------------

    hres = CoSetProxyBlanket(
       pSvc,                        // Indicates the proxy to set
       RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
       NULL,                        // Server principal name 
       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                        // client identity
       EOAC_NONE                    // proxy capabilities 
    );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }
	
	char szVal[64];
	bool done(false);
	while(!done){
		//Get the initial Values
		if(GetValues(pSvc,ulinitVal,(nproc+1)*2)){
			delete ulinitVal;
			done = true;
			continue;
		}

		Sleep(1000);
		//Get the initial Values
		GetValues(pSvc,ulVal,(nproc+1)*2);


		EnterCriticalSection(&CriticalSectionPerfData);
		g_PerfMonData.num_balls = PhysMan._balls.size();
		g_PerfMonData.cur_fps = FPSMan.GetFPS();
		g_PerfMonData.upd_dir = 0;
		for(int j(0); j<9; j++){
			g_PerfMonData.cpu_usage[j] = 0;
		}
		int j(0);
		for(i = 0; i<nproc; i++){
			j = i*2;
			if((ulVal[j+1]- ulinitVal[j+1])>0){
				//sprintf(szVal,"%0.2f",fabs(100.0- ((float)(ulVal[0] - ulinitVal[0]) / (ulVal[1]- ulinitVal[1])) * 100));
				//cout<< "PercentProcessorTime CPU 0: \t\t" <<szVal<<endl;
				g_PerfMonData.cpu_usage[i] = fabs(100.0- ((float)(ulVal[j] - ulinitVal[j]) / (ulVal[j+1]- ulinitVal[j+1])) * 100);
			}
			else{
				//cout<<"Divide by zero for Processor 0"<<endl;
				g_PerfMonData.cpu_usage[i] = 0;
			}
		}
		//i++;
		j=i*2;

		if((ulVal[j+1] - ulinitVal[j+1])>0){
			g_PerfMonData.cpu_usage[8] = fabs(100.0- ((float)(ulVal[j] - ulinitVal[j]) / (ulVal[j+1]- ulinitVal[j+1])) * 100);
		}
		else{
			g_PerfMonData.cpu_usage[8] = 0; // 8 hrd coded as total cpu usage.
		}
		LeaveCriticalSection(&CriticalSectionPerfData);
		//if(nproc>1)
		//{
		//	if((ulVal[3]- ulinitVal[3])>0)
		//	{
		//		sprintf(szVal,"%0.2f",fabs(100.0- ((float)(ulVal[2] - ulinitVal[2]) / (ulVal[3]- ulinitVal[3])) * 100));
		//		cout<< "PercentProcessorTime CPU 1:\t\t" <<szVal<<endl;
		//	}
		//	else
		//	{
		//		cout<<"Divide by zero for Processor 1"<<endl;
		//	}
		//
		//	if((ulVal[5]- ulinitVal[5])>0)
		//	{
		//		sprintf(szVal,"%0.2f",fabs(100.0- ((float)(ulVal[4] - ulinitVal[4]) / (ulVal[5]- ulinitVal[5])) * 100));	
		//		cout<< "PercentProcessorTime  CPU Total:\t" <<szVal<<endl;
		//	}
		//	else
		//	{
		//		cout<<"Divide by zero for total"<<endl;
		//	}
		//}
		//else
		//{
		//	if((ulVal[3]- ulinitVal[3])>0)
		//	{
		//		sprintf(szVal,"%0.2f",fabs(100.0-((float)(ulVal[2] - ulinitVal[2]) / (ulVal[3]- ulinitVal[3])) * 100));
		//		cout<< "PercentProcessorTime  CPU Total:\t" << szVal<<endl;
		//	}
		//	else
		//	{
		//		cout<<"Divide by zero for total"<<endl;
		//	}
		//}

		//0,1 = CPU 0
		//cpu number+0, cpu number+1 = CPU number
		//numberofcpus+2, numberofcpus+3 = total cpu time.

	}
	delete ulinitVal;
	delete ulVal;

   
    // Cleanup
    // ========
    
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();

	//getchar();
    return 0;   // Program successfully completed.

}